import * as THREE from "three";
import Bead from "./Bead";
import { GLTFLoader } from "three/addons/Addons.js";
import { ColiData } from "./BraceleData";
import BeadPool from "./BeadPool";

/**
 * 线圈
 */
export default class Coli {
    /** @param {Bead[]} beads */
    beads = [];
    beadPool = null;
    usedCircumference = 0;  // 已有珠子的总长度
    curAngle = 0;  // 当前珠子的角度
    _beadSpacing = 0; // 珠子之间的间隔

    /**
     * @param {ColiData} data
     */
    constructor(data) {
        this.circumference = data.circumference;
        this.color = data.color;
        this.path = data.path;
        this.beadPool = new BeadPool();

        this.mesh = new THREE.Group();
        // this.mesh.rotation.x = Math.PI / 2;
        this.loader = new GLTFLoader();
        this.loader.load(this.path, (gltf) => {
            const scene = gltf.scene;
            this.mesh.add(scene);
            this.mesh.scale.set(this.radius, this.radius, this.radius);
            this.scene = scene;
        });
    }

    get circumference() {
        return this._circumference;
    }

    set circumference(value) {
        this._circumference = value;
        this.radius = value / 2 / Math.PI;
        if (this.mesh) {
            this.mesh.scale.set(this.radius, this.radius, this.radius);
        }

        this.recalcBead();
    }

    get beadSpacing() {
        return this._beadSpacing;
    }

    /**
     * 设置珠子之间的间隔
     */
    set beadSpacing(value) {
        this._beadSpacing = value;
        const allBeadCircumference = this.beads.reduce((sum, bead) => sum + bead.radius * 2 + this.beadSpacing, 0);
        if (this.circumference < allBeadCircumference) {
            // 超出长度了，重新计算长度
            this.circumference = allBeadCircumference;
            return;
        }

        this.recalcBead();
    }

    /**
     * 重新计算所有珠子的位置
    */
    recalcBead() {
        this.usedCircumference = 0;
        this.curAngle = 0;

        // 重新计算所有珠子的位置
        for (let i = 0; i < this.beads.length; i++) {
            const bead = this.beads[i];
            const { x, y, rotationX, rotationZ } = this.calcTransform(bead.radius);

            // 设置珠子的位置
            bead.mesh.position.set(x, y, 0);

            // 设置珠子的旋转角度，使其垂直于线圈平面
            bead.mesh.rotation.x = rotationX; // 使珠子垂直于线圈平面
            bead.mesh.rotation.z = rotationZ; // 使珠子的开口朝向切线方向

            // 更新总长度和起始角度
            this.usedCircumference += bead.radius * 2 + this.beadSpacing;
            this.curAngle = (this.usedCircumference / this.circumference) * Math.PI * 2;
        }
    }

    /**
     * 添加一个珠子
     * @param {number} radius 珠子的半径
     * @param {number} color 珠子的颜色
     * @returns {number} 所有珠子已经使用的长度
     */
    addBead(beadData) {
        const radius = beadData.radius;
        let allBeadCircumference = this.usedCircumference + radius * 2 + this.beadSpacing;
        if (this.circumference < allBeadCircumference) {
            // 超出长度了，重新计算长度
            this.circumference = allBeadCircumference;
            return this.addBead(beadData);
        }

        let bead = this.beadPool.pop(beadData);
        this.mesh.add(bead.mesh);

        const { x, y, rotationX, rotationZ } = this.calcTransform(radius);
        // 设置珠子的位置
        bead.mesh.position.set(x, y, 0);

        // 设置珠子的旋转角度，使其垂直于线圈平面
        bead.mesh.rotation.x = rotationX; // 使珠子垂直于线圈平面
        bead.mesh.rotation.z = rotationZ; // 使珠子的开口朝向切线方向

        // 更新总长度和起始角度
        this.usedCircumference = allBeadCircumference;
        this.curAngle = (this.usedCircumference / this.circumference) * Math.PI * 2;

        this.beads.push(bead);
        console.log(this.circumference, this.usedCircumference, this.curAngle);
        return this.usedCircumference;
    }

    canCut() {
        if (this.beads.length <= 2) {
            return false;
        }

        // 不能裁切之后让珠子嵌入到珠子中
        let allBeadCircumference = this.beads.reduce((sum, bead) => sum + bead.radius * 2 + this.beadSpacing, 0);
        let radius = allBeadCircumference / 2 / Math.PI;
        for (let i = 0; i < this.beads.length; i++) {
            const bead = this.beads[i];
            if (bead.radius * 2 > radius) {
                return false;
            }
        }

        return true;
    }

    /**
     * 裁切线圈，将多余的部分剔除，将当前珠子所用尺寸设置为周长
     */
    cut() {
        if (!this.canCut()) {
            return;
        }

        let allBeadCircumference = this.beads.reduce((sum, bead) => sum + bead.radius * 2 + this.beadSpacing, 0);
        this.circumference = allBeadCircumference;
        return this.circumference;
    }

    // 是否可以撤销一个珠子
    canBackout() {
        return this.beads.length > 0;
    }

    /**
     * 撤销一个珠子
     * @returns {number} 所有珠子已经使用的长度
     */
    backout() {
        let bead = this.beads.pop();
        if (!bead) {
            return;
        }

        // 更新总长度和起始角度
        this.usedCircumference -= bead.radius * 2 + this.beadSpacing;
        this.curAngle = (this.usedCircumference / this.circumference) * Math.PI * 2;
        
        this.mesh.remove(bead.mesh);
        this.beadPool.push(bead);
        return this.usedCircumference;
    }

    /**
     * 设置线圈的颜色
     * @param {number} color 十六进制数值（例如：0xffffff）
     */
    setColor(color) {
        if (this.scene) {
            this.scene.material.color = new THREE.Color(color);
        }
    }

    /**
     * 计算当前珠子的位置和旋转
     * @param {number} radius 当前要计算的珠子的半径
     * @returns 
     */
    calcTransform(radius) {
        // 计算当前珠子的中心位置（弧度）
        const beadStartAngle = (this.usedCircumference / this.circumference) * Math.PI * 2;
        const centerAngle = beadStartAngle + ((radius * 2 + this.beadSpacing) / 2 / this.circumference) * Math.PI * 2;

        // 计算珠子的位置 - 直接使用线圈半径，不再减去珠子半径
        const x = this.radius * Math.cos(centerAngle);
        const y = this.radius * Math.sin(centerAngle);
        const rotationX = Math.PI / 2; // 使珠子垂直于线圈平面
        const rotationZ = centerAngle + Math.PI / 2; // 使珠子的开口朝向切线方向
        return {
            x, y, rotationX, rotationZ
        }
    }

    getData() {
        return {
            coli: {
                circumference: this.circumference,
                color: this.color,
                path: this.path
            },
            beads: this.beads.map(bead => bead.getData())
        }
    }

    setData(data) {
        this.coli = data.coli;
        this.beads = data.beads;

        this.beads.forEach(bead => {
            bead.setData();
        });

        this.coli.setData();
        return this.circumference;
    }
}